package utils

import (
	"fmt"
	"math/rand"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"encoding/json"
)

const varPatternLeft = "${"
const varPatternRight = "}"

func init() {
	rand.Seed(time.Now().UnixNano())
}

const randomStrOptions = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
const randomStrOptionsLen = len(randomStrOptions)

func RandomStr(num int) string {
	var builder strings.Builder
	for i := 0; i < num; i++ {
		builder.WriteByte(randomStrOptions[rand.Intn(randomStrOptionsLen)])
	}
	return builder.String()
}

func ToString(input interface{}) string {
	if input == nil {
		return ""
	}
	switch v := input.(type) {
	case string:
		return v
	case bool:
		return strconv.FormatBool(v)
	case float64:
		ft := input.(float64)
		return strconv.FormatFloat(ft, 'f', -1, 64)
	case float32:
		ft := input.(float32)
		return strconv.FormatFloat(float64(ft), 'f', -1, 32)
	case int:
		return strconv.Itoa(v)
	case uint:
		return strconv.Itoa(int(v))
	case int8:
		return strconv.Itoa(int(v))
	case uint8:
		return strconv.Itoa(int(v))
	case int16:
		return strconv.Itoa(int(v))
	case uint16:
		return strconv.Itoa(int(v))
	case int32:
		return strconv.Itoa(int(v))
	case uint32:
		return strconv.Itoa(int(v))
	case int64:
		return strconv.FormatInt(v, 10)
	case uint64:
		return strconv.FormatUint(v, 10)
	case []byte:
		return string(v)
	case fmt.Stringer:
		return v.String()
	case error:
		return v.Error()
	default:
		if newValue, err := json.Marshal(input); err == nil {
			return string(newValue)
		} else {
			return ""
		}
	}
}

func ParseJSFunctionParams(function string) []string {
	re := regexp.MustCompile(`function\s*[a-zA-Z0-9_]*\s*\(([a-zA-Z0-9_,\s]*)\)`)
	matches := re.FindStringSubmatch(function)
	if len(matches) < 2 {
		return nil
	}
	params := make([]string, 0)
	paramStr := matches[1]
	paramStr = regexp.MustCompile(`\s`).ReplaceAllString(paramStr, "")
	if paramStr != "" {
		params = append(params, regexp.MustCompile(`,`).Split(paramStr, -1)...)
	}
	return params
}

func SprintfVarFunc(str string, callback func(matched string) interface{}) string {
	re := regexp.MustCompile(`\$\{(.*?)\}`)
	newStr := re.ReplaceAllStringFunc(str, func(matched string) string {
		var result interface{}
		result = matched[2 : len(matched)-1]
		for {
			result = callback(result.(string))
			if reflect.TypeOf(result).String() != "string" {
				break
			} else if !CheckHasVar(result.(string)) {
				result = `"` + result.(string) + `"`
				break
			}
		}
		return ToString(result)
	})
	return newStr
}

func CheckHasVar(str string) bool {
	re := regexp.MustCompile(`\$\{(.*?)\}`)
	matches := re.FindStringSubmatch(str)
	if len(matches) < 2 {
		return false
	}
	return true
}

func CompareInt(c string, p string) bool {
	pIndex, _ := strconv.Atoi(p)
	cIndex, _ := strconv.Atoi(c)
	return cIndex > pIndex
}
